package com.haiqiu.tools.excel.utils;

import com.haiqiu.tools.common.utils.DateUtils;
import com.haiqiu.tools.common.utils.HttpServletUtils;
import com.haiqiu.tools.excel.annotation.Csv;
import org.apache.commons.csv.CSVFormat;
import org.apache.commons.csv.CSVPrinter;
import org.apache.commons.io.ByteOrderMark;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.lang.reflect.Field;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * @author haiqiu
 * @since  2023/6/28
 * csv文件导入导出
 */
public class ExportCSVUtil {

    private static final Logger logger = LoggerFactory.getLogger(ExportCSVUtil.class);

    /**
     * 写CSV并转换为字节流
     *
     * @param tableHeaderArr 表头
     * @param cellList       数据
     * @return
     */
    public static byte[] writeDataAfterToBytes(String[] tableHeaderArr, List<String> cellList) {
        byte[] bytes = new byte[0];
        ByteArrayOutputStream byteArrayOutputStream = null;
        OutputStreamWriter outputStreamWriter = null;
        BufferedWriter bufferedWriter = null;
        try {
            byteArrayOutputStream = new ByteArrayOutputStream();
            outputStreamWriter = new OutputStreamWriter(byteArrayOutputStream, StandardCharsets.UTF_8);
            bufferedWriter = new BufferedWriter(outputStreamWriter);
            //excel文件需要通过文件头的bom来识别编码，而CSV文件格式不自带bom,所以写文件时，需要先写入bom头，否则excel打开乱码
            bufferedWriter.write(new String(ByteOrderMark.UTF_8.getBytes()));
            //写表头
            StringBuilder sb = new StringBuilder();
            String tableHeader = String.join(",", tableHeaderArr);
            sb.append(tableHeader + StringUtils.CR + StringUtils.LF);
            for (String rowCell : cellList) {
                sb.append(rowCell + StringUtils.CR + StringUtils.LF);
            }
            bufferedWriter.write(sb.toString());
            bufferedWriter.flush();
            //把输出流转换字节流
            bytes = byteArrayOutputStream.toString(StandardCharsets.UTF_8.name()).getBytes();
            return bytes;
        } catch (IOException e) {
            logger.error("writeDataAfterToBytes IOException:{}", e.getMessage(), e);
        } finally {
            try {
                if (bufferedWriter != null) {
                    bufferedWriter.close();
                }
                if (outputStreamWriter != null) {
                    outputStreamWriter.close();
                }
                if (byteArrayOutputStream != null) {
                    byteArrayOutputStream.close();
                }
            } catch (IOException e) {
                logger.error("iostream close IOException:{}", e.getMessage(), e);
            }
        }
        return bytes;
    }

    /**
     * 写CSV并转换为字节流
     *
     * @param headers  表头
     * @param cellList 表数据
     * @return
     */
    public static byte[] writeCsvAfterToBytes(String[] headers, List<Object[]> cellList) {
        byte[] bytes = new byte[0];
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        OutputStreamWriter outputStreamWriter = new OutputStreamWriter(byteArrayOutputStream, StandardCharsets.UTF_8);
        BufferedWriter bufferedWriter = new BufferedWriter(outputStreamWriter);
        CSVPrinter csvPrinter = null;
        try {
            //创建csvPrinter并设置表格头
            csvPrinter = new CSVPrinter(bufferedWriter, CSVFormat.DEFAULT.withHeader(headers));
            //写数据
            csvPrinter.printRecords(cellList);
            csvPrinter.flush();
            bytes = byteArrayOutputStream.toString(StandardCharsets.UTF_8.name()).getBytes();
        } catch (IOException e) {
            logger.error("writeCsv IOException:{}", e.getMessage(), e);
        } finally {
            try {
                if (csvPrinter != null) {
                    csvPrinter.close();
                }
                if (bufferedWriter != null) {
                    bufferedWriter.close();
                }
                if (outputStreamWriter != null) {
                    outputStreamWriter.close();
                }
                if (byteArrayOutputStream != null) {
                    byteArrayOutputStream.close();
                }
            } catch (IOException e) {
                logger.error("iostream close IOException:{}", e.getMessage(), e);
            }
        }
        return bytes;
    }

    
    public static <T> void writeCsvAfterToFiles(Class<T> clazz, List<T> list,String fileName) {
        byte[] bytes = writeCsvAfterToBytes(clazz, list);
        ExportCSVUtil.responseSetProperties(fileName, bytes, HttpServletUtils.getServletResponse());
    }

    
    public static <T> byte[] writeCsvAfterToBytes(Class<T> clazz, List<T> list) {
        try {
            if (list.size() == 0) {
                throw new RuntimeException("数据为空");
            }
            // 得到所有定义字段
            List<String> headers = new ArrayList<>();
            List<Field> fields = getAllFields(clazz);
            for (Field field : fields) {
                String name = field.getAnnotation(Csv.class).name();
                if (name == null) {
                    headers.add(field.getName());
                } else {
                    headers.add(name);
                }
            }
            List<Object[]> cellList = new ArrayList<>();
            for (T t : list) {
                List<Object> objects = new ArrayList<>();
                for (Field field : fields) {
                    field.setAccessible(true);
                    Csv attr = field.getAnnotation(Csv.class);
                    Object o = field.get(t);
                    //替换需要配置的数据列字典值
                    if (attr.replace().length > 0 && field.get(t) != null) {
                        for (String data : attr.replace()) {
                            String prefix = data.substring(0, data.lastIndexOf("_"));
                            String suffix = data.substring(data.lastIndexOf("_") + 1);
                            if (prefix.equals(field.get(t).toString())) {
                                o = suffix;
                                break;
                            }
                        }
                    }
                    //设置时间格式
                    if (field.get(t) != null && field.get(t).getClass() != null && Date.class.equals(field.get(t).getClass())) {
                        //设置自定义注解的时间格式
                        if (attr.dateFormat() != null) {
                            o = DateUtils.format((Date) o, attr.dateFormat());
                        }
                    }
                    objects.add(o);
                }
                cellList.add(objects.toArray());
            }
            String[] array = headers.toArray(new String[headers.size()]);
            return writeCsvAfterToBytes(array, cellList);
        }catch (Exception e){
            e.printStackTrace();
            throw new RuntimeException(e.getMessage());
        }
    }

    /**
     * 获取反射后的注解字段
     *
     * @param clazz 类
     * @return 字段
     */
    public static <T> List<Field> getAllFields(Class<T> clazz) {
        List<Field> fieldList = new ArrayList<>();
        while (clazz != null) {
            List<Field> fields = getFields(clazz.getDeclaredFields());
            fieldList.addAll(fields);
            clazz = (Class<T>) clazz.getSuperclass();
        }
        return fieldList;
    }

    /**
     * 获取反射过后的数据
     *
     * @param clazz 泛型数据
     * @return 成员列表
     */
    private static List<Field> getFields(Field[] clazz) {
        List<Field> fields = new ArrayList<>();
        // 得到所有field并存放到一个list中
        for (Field field : clazz) {
            if (field.isAnnotationPresent(Csv.class)) {
                fields.add(field);
            }
        }
        return fields;
    }

    /**
     * 设置下载响应
     *
     * @param fileName
     * @param bytes
     * @param response
     */
    public static void responseSetProperties(String fileName, byte[] bytes, HttpServletResponse response) {
        try {
            fileName = URLEncoder.encode(fileName, StandardCharsets.UTF_8.name());
            response.setContentType("application/csv");
            response.setCharacterEncoding(StandardCharsets.UTF_8.name());
            response.setHeader("Pragma", "public");
            response.setHeader("Cache-Control", "max-age=30");
            response.setHeader("Content-Disposition", "attachment; filename=" + fileName);
            OutputStream outputStream = response.getOutputStream();
            //加上UTF-8文件的标识字符
            outputStream.write(new   byte []{( byte ) 0xEF ,( byte ) 0xBB ,( byte ) 0xBF });
            outputStream.write(bytes);
            outputStream.flush();
        } catch (IOException e) {
            logger.error("iostream error:{}", e.getMessage(), e);
        }
    }

}